package com.xlf.service.judge.strategy.judgeMachine.impl.goJudge;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.xlf.common.core.util.StringUtils;
import com.xlf.service.judge.exception.CompileError;
import com.xlf.service.judge.io.File;
import com.xlf.service.judge.io.impl.LocalFile;
import com.xlf.service.judge.strategy.judgeMachine.impl.goJudge.builder.CompileCmdBuilder;
import com.xlf.service.judge.strategy.judgeMachine.impl.goJudge.builder.RunCmdBuilder;
import com.xlf.service.judge.pojo.LanguageConfig;
import com.xlf.service.judge.io.LanguageConfigLoader;
import com.xlf.pojo.constant.Constants;
import com.xlf.service.judge.io.ResourceLoader;
import com.xlf.service.judge.pojo.RunDTO;
import com.xlf.pojo.dto.RunResult;
import com.xlf.service.judge.strategy.judgeMachine.AbstractJudgeMachineStrategy;

import java.util.*;

import static com.xlf.pojo.constant.Constants.Judge.STATUS_RUNTIME_ERROR;
import static com.xlf.pojo.constant.Constants.Judge.STATUS_SYSTEM_ERROR;

/**
 * @Author xlf 2734029211@qq.com
 * @Date 2023/9/15 23:29
 * @Description: go判题机
 * @Version 1.0
 */
public class GoJudgeMachine extends AbstractJudgeMachineStrategy {

    private static String url = "http://localhost:5050";



    public static final HashMap<String, Constants.Judge> RESULT_MAP_STATUS = new HashMap<>();


    public void setUrl(String url) {
        this.url = url;
    }
    static {

        RESULT_MAP_STATUS.put("Time Limit Exceeded", Constants.Judge.STATUS_TIME_LIMIT_EXCEEDED);
        RESULT_MAP_STATUS.put("Memory Limit Exceeded", Constants.Judge.STATUS_MEMORY_LIMIT_EXCEEDED);
        RESULT_MAP_STATUS.put("Output Limit Exceeded", STATUS_RUNTIME_ERROR);
        RESULT_MAP_STATUS.put("Accepted", Constants.Judge.STATUS_ACCEPTED);
        RESULT_MAP_STATUS.put("Nonzero Exit Status", STATUS_RUNTIME_ERROR);
        RESULT_MAP_STATUS.put("Internal Error", STATUS_SYSTEM_ERROR);
        RESULT_MAP_STATUS.put("File Error", STATUS_SYSTEM_ERROR);
        RESULT_MAP_STATUS.put("Signalled", STATUS_RUNTIME_ERROR);
    }

    public GoJudgeMachine(LanguageConfigLoader languageConfigLoader, ResourceLoader resourceLoader) {
        super(languageConfigLoader, resourceLoader);
    }

    @Override
    protected String compile(LanguageConfig languageConfig, String code) throws CompileError {

        HashMap<String, String> copyIn = new HashMap<>();
        copyIn.put(languageConfig.getSrcName(),code);

        JSONObject cmdItem = new CompileCmdBuilder(
                translateCommandline(languageConfig.getCompileCommand()),
                languageConfig.getCompileEnvs(),
                languageConfig.getMaxCpuTime(),
                languageConfig.getMaxRealTime(),
                languageConfig.getMaxMemory(),
                languageConfig.getMaxMemory(),
                copyIn,
                Collections.singletonList(languageConfig.getExeName()))
                .Commander();

        JSONArray jsonArray = new JSONArray();
        jsonArray.add(cmdItem);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("cmd",jsonArray);
        String cmd = JSON.toJSONString(jsonObject);

//        cmd = cmd.replaceAll("\\\\\\\\\\\\\\\\n", "\n");
//        cmd = cmd.replaceAll("\\\\\\\\\\\\\\\\t", "\t");

        JSONObject res = (JSONObject) JSON.parseArray(HttpUtil.post(url + "/run", cmd)).get(0);
        Constants.Judge status = RESULT_MAP_STATUS.get(res.getString("status"));
        if(Constants.Judge.STATUS_ACCEPTED.equals(status)){
            return res.getJSONObject("fileIds").getString(languageConfig.getExeName());
        }else {
            //编程错误
            throw new CompileError("Compile Error",res.getJSONObject("files").getString("stderr"));
        }
    }


    @Override
    protected RunResult run(RunDTO runDTO) {

        LanguageConfig languageConfig = runDTO.getLanguageConfig();
        HashMap<String, String> copyIn = new HashMap<>();
        copyIn.put(languageConfig.getExeName(),(String) runDTO.getFile());


        JSONObject cmdItem = new RunCmdBuilder(
                translateCommandline(languageConfig.getRunCommand()),
                languageConfig.getCompileEnvs(),
                runDTO.getRunLimit(),
                copyIn,
                runDTO.getTestCaseInput(),
                null)
                .Commander();

        JSONArray jsonArray = new JSONArray();
        jsonArray.add(cmdItem);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("cmd",jsonArray);
        String cmd = JSON.toJSONString(jsonObject);


        RunResult runResult = new RunResult();
        JSONObject res = null;

        res = (JSONObject) JSON.parseArray(HttpUtil.post(url + "/run", cmd)).get(0);
        String status = res.getString("status");
        runResult.setStatus(RESULT_MAP_STATUS.get(status).getStatus());
        runResult.setMemory((int) (res.getLong("memory")/1024));
        //ns -> ms
        runResult.setTime((int) (res.getLong("time")/1000000));
        runResult.setRunTime((int) (res.getLong("runTime")/1000000));

        if("Accepted".equals(status)) {
            runResult.setFile(res.getJSONObject("files").getString("stdout"));
        }
        else {
            runResult.setMsg(res.getJSONObject("files").getString("stderr"));
        }

        return runResult;

    }

    @Override
    public void afterHandle(Object file, List<RunResult> runResult) {
        if(StringUtils.isNotEmpty((String) file))
            HttpRequest.delete(url + '/' + "file/" + file.toString());

    }


    protected static List<String> translateCommandline(String toProcess) {
        if (toProcess != null && !toProcess.isEmpty()) {
            int state = 0;
            StringTokenizer tok = new StringTokenizer(toProcess, "\"' ", true);
            List<String> result = new ArrayList<>();
            StringBuilder current = new StringBuilder();
            boolean lastTokenHasBeenQuoted = false;

            while (true) {
                while (tok.hasMoreTokens()) {
                    String nextTok = tok.nextToken();
                    switch (state) {
                        case 1:
                            if ("'".equals(nextTok)) {
                                lastTokenHasBeenQuoted = true;
                                state = 0;
                            } else {
                                current.append(nextTok);
                            }
                            continue;
                        case 2:
                            if ("\"".equals(nextTok)) {
                                lastTokenHasBeenQuoted = true;
                                state = 0;
                            } else {
                                current.append(nextTok);
                            }
                            continue;
                    }

                    if ("'".equals(nextTok)) {
                        state = 1;
                    } else if ("\"".equals(nextTok)) {
                        state = 2;
                    } else if (" ".equals(nextTok)) {
                        if (lastTokenHasBeenQuoted || current.length() > 0) {
                            result.add(current.toString());
                            current.setLength(0);
                        }
                    } else {
                        current.append(nextTok);
                    }

                    lastTokenHasBeenQuoted = false;
                }

                if (lastTokenHasBeenQuoted || current.length() > 0) {
                    result.add(current.toString());
                }

                if (state != 1 && state != 2) {
                    return result;
                }

                throw new RuntimeException("unbalanced quotes in " + toProcess);
            }
        } else {
            return new ArrayList<>();
        }
    }


}
